package hu.myai.indicators.trendline;

import hu.myai.api.indicators.AbstractIndicator;
import hu.myai.indicators.pivot.LastHighIndicator;
import hu.myai.indicators.pivot.LastHigherHighIndicator;
import hu.myai.model.IndicatorVO;
import hu.myai.model.PointVO;
import hu.myai.model.TrendLineVO;
import hu.myai.util.CommonUtil;
import hu.myai.util.pool.TrendLinePool;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Return the closest RESISTANCE. The inficator return value contains a key to
 * the TrendLinePool class. Form the pool you can get the line using the
 * singleton: getResistanceInstance().
 * */
public class ResistanceLevelIndicator extends AbstractIndicator {

	/**
	 * The object used when logging debug,errors,warnings and info.
	 * */
	private final Logger log = LoggerFactory.getLogger(this.getClass());

	List<PointVO> lastHighList = new ArrayList<PointVO>();

	public ResistanceLevelIndicator(Double parameter) {
		super(parameter);
	}

	private String highID;

	@Override
	public void init() {
		if (parameter > 0)
			highID = register(new LastHighIndicator(parameter));
		else
			highID = register(new LastHigherHighIndicator(-1 * parameter));
	}

	@Override
	protected IndicatorVO indicatorCalculation() {

		IndicatorVO lhIndicator = indicatorMap.get(highID);
		List<TrendLineVO> poolLineList = TrendLinePool.getResistanceInstance().getLineList(tickSourceID);

		/**
		 * Add new high to the pool and maintain resistance pool list.
		 * */
		if (lhIndicator != null && lhIndicator.isChanged()) {
			lastHighList.add(0, new PointVO(lhIndicator.getValue(), lastTick.getTimestamp() - parameter.intValue()));
			addNewLines(lastHighList);
		}

		/**
		 * remove the unnecessary lines from the pool.
		 * */
		cleanUpLines(poolLineList);

		/**
		 * It is the native indicator function. returning the closest resistance
		 * */
		double minDistance = Double.POSITIVE_INFINITY;
		TrendLineVO resistance = null;
		for (TrendLineVO line : poolLineList) {

			// distance between the high of the last tick and the line
			double distance = line.getY(lastTick.getTimestamp()) - lastTick.getHigh();

			if (distance > 0) {
				// get the closest high to the line
				if (distance < minDistance) {
					minDistance = distance;
					resistance = line;
				}
			}
		}

		if (resistance == null)
			return null;

		IndicatorVO indicator = new IndicatorVO();
		indicator.setValue(resistance.hashCode());
		indicator.setChanged(true);
		indicator.setDate(lastTick.getTime());

		log.debug(indicator.toString());
		return indicator;
	}

	/**
	 * Two pivots defines a line. All line is a potential resistance. Add all
	 * potential resistance to the pool and remove the unnecessay highs and
	 * lines from the lists
	 * */
	private void addNewLines(List<PointVO> highList) {

		double initTangens = Double.POSITIVE_INFINITY;

		for (int i = 1; i < highList.size(); i++) {

			// line between two pivots
			TrendLineVO line = new TrendLineVO(highList.get(0), highList.get(i));
			if (initTangens > line.getTangens()) {
				// no high goes thru the line so add the line to the pool
				initTangens = line.getTangens();
				TrendLinePool.getResistanceInstance().addLine(line, tickSourceID);
			} else {
				// remove the unnecessary highs.
				highList.set(i, null);
			}
		}
		// apply the remove
		CommonUtil.removeNull(highList);
	}

	/**
	 * remove the unnecessary lines from the pool. A line is unnecessary if the
	 * 2 last high is above.
	 * */
	private void cleanUpLines(List<TrendLineVO> lineList) {
		for (int i = 0; i < lineList.size(); i++) {

			double y = lineList.get(i).getY(lastTick.getTimestamp());

			// mark the touched lines
			if (y < lastTick.getLow()) {
				lineList.get(i).increaseTouch();
			}

			// remove the unused lines from the list
			if (lineList.get(i).getTouch() > 2) {
				lineList.set(i, null);
			}
		}
		// apply the remove
		CommonUtil.removeNull(lineList);
	}
}
